﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.Domain.Uow.Database;
using Sgr.Exceptions;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sgr.Domain.Uow
{
    public class DefaultUnitOfWork : IUnitOfWork
    {
        //数据库API字典，键为数据库标识，值为对应的数据库API实例
        private readonly Dictionary<string, IDatabaseApi> _databaseApis;

        //事务API字典，键为事务标识，值为对应的事务API实例
        private readonly Dictionary<string, ITransactionApi> _transactionApis;

        //日志记录器
        private readonly ILogger<DefaultUnitOfWork> _logger;

        /// <summary>
        /// 服务提供者
        /// </summary>
        private readonly IServiceProvider _serviceProvider;

        //异常信息
        private Exception? _exception;

        //是否正在完成
        private bool _isCompleting;

        //是否已回滚
        private bool _isRolledback;

        //工作单元选项
        private UnitOfWorkOption _unitOfWorkOptions;

        //完成后的处理事件列表
        protected List<Func<Task>> CompletedHandlers { get; } = new List<Func<Task>>();

        /// <summary>
        /// 初始化 <see cref="DefaultUnitOfWork"/> 类的新实例
        /// </summary>
        /// <param name="serviceProvider">服务提供者</param>
        /// <param name="options">工作单元配置选项</param>
        /// <param name="logger">日志记录器</param>
        /// <remarks>
        /// 构造函数执行以下操作：
        /// 1. 初始化日志记录器
        /// 2. 设置工作单元选项，如果未提供则使用默认值
        /// 3. 初始化数据库API和事务API的存储容器
        /// </remarks>
        /// <exception cref="ArgumentNullException">当 logger 参数为 null 时抛出</exception>
        public DefaultUnitOfWork(
            IServiceProvider serviceProvider,
            IOptions<UnitOfWorkOption> options,
            ILogger<DefaultUnitOfWork> logger)
        {
            _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(logger));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _unitOfWorkOptions = options.Value ?? new UnitOfWorkOption(true, IsolationLevel.ReadCommitted, 30000);

            _databaseApis = new Dictionary<string, IDatabaseApi>();
            _transactionApis = new Dictionary<string, ITransactionApi>();
        }

        #region IUnitOfWork

        /// <summary>
        /// 工作单元唯一标识
        /// </summary>
        public virtual Guid Id { get; } = Guid.NewGuid();

        /// <summary>
        /// 获取工作单元的选项
        /// </summary>
        public UnitOfWorkOption Options { get; private set; } = default!;

        /// <summary>
        /// 获取外部工作单元
        /// </summary>
        public virtual IUnitOfWork? Outer { get; private set; }

        /// <summary>
        /// 获取服务提供者
        /// </summary>
        public IServiceProvider ServiceProvider
        {
            get
            {
                ThrowIfDisposed();

                //if (this.IsCompleted || this._isRolledback)
                //{
                //    throw new BusinessException("工作单元已完成");
                //}

                return _serviceProvider;
            }
        }

        /// <summary>
        /// 是否是根工作单元
        /// </summary>
        public virtual bool IsRoot => Outer == null;

        /// <summary>
        /// 是否已完成
        /// </summary>
        public virtual bool IsCompleted { get; private set; }

        /// <summary>
        /// 是否已释放
        /// </summary>
        public virtual bool IsDisposed { get; private set; }

        /// <summary>
        /// 初始化工作单元
        /// </summary>
        /// <param name="options">工作单元配置选项</param>
        /// <remarks>
        /// 此方法执行以下操作：
        /// 1. 验证输入参数
        /// 2. 检查工作单元是否已初始化
        /// 3. 合并默认选项和传入选项
        /// </remarks>
        /// <exception cref="ArgumentNullException">当 options 参数为 null 时抛出</exception>
        /// <exception cref="BusinessException">当工作单元已经初始化时抛出</exception>
        public virtual void Initialize(UnitOfWorkOption options)
        {
            Check.NotNull(options, nameof(options));

            if (Options != null)
            {
                throw new BusinessException("当工作单元已经初始化完成.");
            }

            // 合并默认选项
            if (options.IsolationLevel == null)
            {
                options.IsolationLevel = _unitOfWorkOptions.IsolationLevel;
            }

            if (options.Timeout == null)
            {
                options.Timeout = _unitOfWorkOptions.Timeout;
            }

            // 使用合并后的选项
            Options = options;
        }

        /// <summary>
        /// 设置外部工作单元
        /// </summary>
        /// <param name="outer"></param>
        public virtual void SetOuter(IUnitOfWork? outer)
        {
            Outer = outer;
        }

        /// <summary>
        /// 执行Save操作
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            if (_isRolledback)
            {
                return false;
            }

            foreach (var databaseApi in GetAllActiveDatabaseApis())
            {
                if (databaseApi is ISupportsSavingChanges supportsSavingChangesDatabaseApi)
                {
                    await supportsSavingChangesDatabaseApi.SaveChangesAsync(cancellationToken);
                }
            }

            return true;
        }

        /// <summary>
        /// 获取数据库API集合
        /// </summary>
        /// <returns></returns>
        protected virtual IReadOnlyList<IDatabaseApi> GetAllActiveDatabaseApis()
        {
            return _databaseApis.Values.ToImmutableList();
        }

        #endregion IUnitOfWork

        #region IDatabaseApiContainer

        /// <summary>
        /// 添加数据库API到容器中
        /// </summary>
        /// <param name="key">数据库API的唯一标识键</param>
        /// <param name="api">要添加的数据库API实例</param>
        /// <remarks>
        /// 如果指定的键已存在，则抛出异常
        /// </remarks>
        public virtual void AddDatabaseApi(string key, IDatabaseApi api)
        {
            ThrowIfDisposed();

            Check.StringNotNullOrEmpty(key, nameof(key));
            Check.NotNull(api, nameof(api));

            if (_databaseApis.ContainsKey(key))
            {
                throw new BusinessException($"工作单元已包含标识为 '{key}' 的数据库API。");
            }

            _databaseApis.Add(key, api);

#if DEBUG
            if (_databaseApis.Count > 5)
                throw new BusinessException("数据库API数量超过5个");
#endif
        }

        /// <summary>
        /// 获取指定键的数据库API，如果不存在则使用工厂方法创建
        /// </summary>
        /// <param name="key">数据库API的唯一标识键</param>
        /// <param name="factory">用于创建新数据库API实例的工厂方法</param>
        /// <remarks>
        /// 此方法确保返回一个有效的数据库API实例：
        /// 1. 如果指定键的API已存在，则返回现有实例
        /// 2. 如果不存在，则使用工厂方法创建新实例并添加到容器中
        /// key参数区分大小写，建议使用小写形式
        /// </remarks>
        /// <returns>获取到的或新创建的数据库API实例</returns>
        public virtual IDatabaseApi GetOrAddDatabaseApi(string key, Func<IDatabaseApi> factory)
        {
            ThrowIfDisposed();

            Check.StringNotNullOrEmpty(key, nameof(key));
            Check.NotNull(factory, nameof(factory));

            return _databaseApis.GetOrAdd(key, factory);
        }

        /// <summary>
        /// 查找指定键的数据库API
        /// </summary>
        /// <param name="key">数据库API的唯一标识键</param>
        /// <remarks>
        /// 如果指定的键不存在，则返回null
        /// </remarks>
        /// <returns>找到的数据库API实例，如果未找到则返回null</returns>
        public virtual IDatabaseApi? FindDatabaseApi(string key)
        {
            return _databaseApis.GetOrDefault(key);
        }

        #endregion IDatabaseApiContainer

        #region ITransactionApiContainer

        /// <summary>
        /// 获取当前容器中事务API的总数
        /// </summary>
        /// <returns>已注册的事务API数量</returns>
        public virtual int CountTransactionApi()
        {
            return _transactionApis.Count;
        }

        /// <summary>
        /// 判断指定键的事务API是否存在
        /// </summary>
        /// <param name="key">事务API的唯一标识键</param>
        /// <remarks>
        /// 检查容器中是否包含指定键的事务API：
        /// - 建议使用统一的命名规范，如：{数据库名}.{事务类型}
        /// - 仅检查是否存在，不返回具体实例
        /// </remarks>
        /// <returns>如果存在返回true，否则返回false</returns>
        public virtual bool AnyTransactionApi(string key)
        {
            return _transactionApis.Any(x => x.Key == key);
        }

        /// <summary>
        /// 查找指定键的事务API
        /// </summary>
        /// <param name="key">事务API的唯一标识键（例如："database1.transaction"）</param>
        /// <remarks>
        /// 用于查找已注册的事务API：
        /// - key参数区分大小写
        /// - 建议使用统一的命名规范，如：{数据库名}.{事务类型}
        /// - 如果未找到指定的事务API，返回null
        /// </remarks>
        /// <returns>找到的事务API实例，如果未找到则返回null</returns>
        public virtual ITransactionApi? FindTransactionApi(string key)
        {
            Check.StringNotNullOrEmpty(key, nameof(key));

            return _transactionApis.GetOrDefault(key);
        }

        /// <summary>
        /// 添加事务API到容器中
        /// </summary>
        /// <param name="key">事务API的唯一标识键</param>
        /// <param name="api">要添加的事务API实例</param>
        /// <remarks>
        /// 添加新的事务API实例：
        /// - 如果key已存在，将抛出异常
        /// - api实例必须是有效的（非null）
        /// - 添加后的api实例由容器负责管理其生命周期
        /// </remarks>
        public virtual void AddTransactionApi(string key, ITransactionApi api)
        {
            Check.StringNotNullOrEmpty(key, nameof(key));
            Check.NotNull(api, nameof(api));

            if (_transactionApis.ContainsKey(key))
            {
                throw new BusinessException($"工作单元已包含标识为 '{key}' 的事务API。");
            }

            _transactionApis.Add(key, api);

#if DEBUG
            if (_transactionApis.Count > 5)
                throw new BusinessException("事务API数量超过5个");
#endif
        }

        /// <summary>
        /// 获取或创建事务API
        /// </summary>
        /// <param name="key">事务API的唯一标识键</param>
        /// <param name="factory">创建事务API的工厂方法</param>
        /// <remarks>
        /// 获取或创建事务API实例：
        /// 1. 首先尝试获取已存在的事务API
        /// 2. 如果不存在，则使用工厂方法创建新实例
        /// 3. 新创建的实例会自动添加到容器中
        ///
        /// 注意：
        /// - factory不应返回null
        /// - 创建的实例应该是线程安全的
        /// - 实例的释放由容器负责管理
        /// </remarks>
        /// <returns>已存在的或新创建的事务API实例</returns>
        public virtual ITransactionApi GetOrAddTransactionApi(string key, Func<ITransactionApi> factory)
        {
            Check.StringNotNullOrEmpty(key, nameof(key));
            Check.NotNull(factory, nameof(factory));

            return _transactionApis.GetOrAdd(key, factory);
        }

        #endregion ITransactionApiContainer

        #region IUnitOfWorkTransactions

        /// <summary>
        /// 异步提交工作单元
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <remarks>
        /// 该方法执行以下操作：
        /// 1. 检查工作单元状态
        /// 2. 保存所有实体更改
        /// 3. 提交所有活动事务
        /// 4. 执行完成后的处理事件
        /// </remarks>
        /// <exception cref="ObjectDisposedException">工作单元已释放时抛出</exception>
        /// <exception cref="BusinessException">工作单元已完成或正在完成时抛出</exception>
        /// <returns>表示异步操作的任务</returns>
        public virtual async Task CommitAsync(CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            if (_isRolledback)
            {
                _logger.LogWarning("工作单元 {Id} 已回滚，跳过提交操作", Id);
                return;
            }

            PreventMultipleComplete();

            try
            {
                _isCompleting = true;

                // 保存实体更改
                await SaveEntitiesAsync(cancellationToken);

                // 提交事务
                await CommitTransactionsAsync(cancellationToken);

                IsCompleted = true;

                // 执行完成后的处理事件
                await OnCompletedAsync();
            }
            catch (Exception ex)
            {
                // 使用结构化日志记录更详细的工作单元提交失败信息
                _logger.LogError(/*ex,*/
                    "工作单元 {UnitOfWorkId} 提交失败。事务状态: {IsTransactional}, 隔离级别: {IsolationLevel}, 超时设置: {Timeout}ms, 是否根工作单元: {IsRoot}, 活动事务数: {TransactionCount}",
                    Id,
                    Options?.IsTransactional,
                    Options?.IsolationLevel,
                    Options?.Timeout,
                    IsRoot,
                    _transactionApis.Count);

                // 保存异常引用以供后续处理（如在Dispose方法中触发Failed事件）
                _exception = ex;

                // 确保异常信息被正确传播出去
                throw;
            }
        }

        /// <summary>
        /// 异步回滚事务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task RollbackAsync(CancellationToken cancellationToken = default)
        {
            if (_isRolledback)
            {
                return;
            }

            _isRolledback = true;

            await RollbackAllAsync(cancellationToken);
        }

        /// <summary>
        /// 防止多次完成工作单元
        /// </summary>
        /// <exception cref="BusinessException">当工作单元已完成或正在完成时抛出</exception>
        private void PreventMultipleComplete()
        {
            if (IsCompleted || _isCompleting)
            {
                throw new BusinessException("此工作单元已请求完成。");
            }
        }

        /// <summary>
        /// 提交所有活动事务
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <remarks>
        /// 此方法会遍历并提交所有当前活动的事务。
        /// 按照事务注册的顺序依次提交每个事务。
        /// </remarks>
        /// <returns>表示异步操作的任务</returns>
        protected virtual async Task CommitTransactionsAsync(CancellationToken cancellationToken)
        {
            foreach (var transaction in GetAllActiveTransactionApis())
            {
                await transaction.CommitAsync(cancellationToken);
            }
        }

        /// <summary>
        /// 回滚所有活动的事务和数据库操作
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <remarks>
        /// 此方法按以下顺序执行回滚：
        /// 1. 首先回滚所有活动事务
        /// 2. 然后回滚所有数据库更改
        /// 如果回滚过程中发生异常，将记录日志但继续执行其他回滚操作
        /// </remarks>
        /// <returns>表示异步回滚操作的任务</returns>
        protected virtual async Task RollbackAllAsync(CancellationToken cancellationToken)
        {
            // 首先回滚事务
            foreach (var transactionApi in GetAllActiveTransactionApis())
            {
                if (transactionApi is ISupportsRollback supportsRollbackTransactionApi)
                {
                    try
                    {
                        await supportsRollbackTransactionApi.RollbackAsync(cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "回滚事务 {TransactionType} 时发生异常", transactionApi.GetType().Name);
                    }
                }
            }

            // 然后回滚数据库更改
            foreach (var databaseApi in GetAllActiveDatabaseApis())
            {
                if (databaseApi is ISupportsRollback supportsRollbackDatabaseApi)
                {
                    try
                    {
                        await supportsRollbackDatabaseApi.RollbackAsync(cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "回滚数据库 {DatabaseType} 时发生异常", databaseApi.GetType().Name);
                    }
                }
            }
        }

        /// <summary>
        /// 获取所有活动的事务API列表
        /// </summary>
        /// <remarks>
        /// 返回当前工作单元中所有活动的事务API的不可变列表。
        /// 通过ToImmutableList确保返回的集合不可被修改，提高数据安全性。
        /// </remarks>
        /// <returns>活动事务API的只读列表</returns>
        public virtual IReadOnlyList<ITransactionApi> GetAllActiveTransactionApis()
        {
            return _transactionApis.Values.ToImmutableList();
        }

        #endregion IUnitOfWorkTransactions

        #region IUnitOfWorkEventHandling

        /// <summary>
        /// 工作单元失败事件
        /// </summary>
        /// <remarks>
        /// 当工作单元执行失败时触发此事件，用于通知监听者工作单元执行失败的情况
        /// </remarks>
        public event EventHandler<UnitOfWorkFailedEventArgs> Failed = default!;

        /// <summary>
        /// 工作单元释放事件
        /// </summary>
        /// <remarks>
        /// 当工作单元被释放时触发此事件，用于通知监听者工作单元即将被释放的情况
        /// </remarks>
        public event EventHandler<UnitOfWorkEventArgs> Disposed = default!;

        /// <summary>
        /// 添加事务提交成功后的处理事件
        /// </summary>
        /// <param name="handler">异步事件处理器</param>
        /// <remarks>
        /// 添加一个在工作单元成功完成后要执行的异步处理程序
        /// 这些处理程序将在工作单元提交成功后按添加顺序执行
        /// </remarks>
        public virtual void AddCompletedHandler(Func<Task> handler)
        {
            Check.NotNull(handler, nameof(handler));

            ThrowIfDisposed();

            CompletedHandlers.Add(handler);
        }

        /// <summary>
        /// 执行所有完成后的处理事件
        /// </summary>
        /// <remarks>
        /// 按照注册顺序异步执行所有已添加的完成处理程序
        /// 此方法在工作单元成功完成后由 CommitAsync 方法调用
        /// </remarks>
        /// <returns>表示异步操作的任务</returns>
        protected virtual async Task OnCompletedAsync()
        {
            var handlers = CompletedHandlers.ToArray();

            foreach (var handler in handlers)
            {
                try
                {
                    await handler.Invoke().ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "执行工作单元 {Id} 完成后的处理事件发生异常 ", this.Id);
                }
            }
        }

        #endregion IUnitOfWorkEventHandling

        #region IDisposable

        /// <summary>
        /// 释放工作单元的所有资源
        /// </summary>
        /// <remarks>
        /// 实现 IDisposable 接口，确保资源被正确释放
        /// </remarks>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源的核心方法
        /// </summary>
        /// <param name="disposing">
        /// 是否为显式释放
        /// true: 通过 Dispose() 方法调用
        /// false: 通过终结器调用
        /// </param>
        /// <remarks>
        /// 执行以下操作：
        /// 1. 释放所有活动事务
        /// 2. 触发失败事件（如果需要）
        /// 3. 触发释放事件
        /// </remarks>
        protected virtual void Dispose(bool disposing)
        {
            if (IsDisposed)
                return;

            if (disposing)
            {
                IsDisposed = true;

                try
                {
                    DisposeTransactions();

                    _transactionApis.Clear();
                    _databaseApis.Clear();
                    CompletedHandlers.Clear();

                    if (!IsCompleted || _exception != null)
                    {
                        OnFailed();
                    }

                    OnDisposed();
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "工作单元 {Id} 释放资源时发生异常", Id);
                }
            }
        }

        /// <summary>
        /// 触发工作单元释放事件
        /// </summary>
        /// <remarks>
        /// 通知所有监听者工作单元即将被释放
        /// </remarks>
        protected virtual void OnDisposed()
        {
            Disposed?.Invoke(this, new UnitOfWorkEventArgs(this));
        }

        /// <summary>
        /// 触发工作单元失败事件
        /// </summary>
        /// <remarks>
        /// 当工作单元未正常完成时通知监听者
        /// </remarks>
        protected virtual void OnFailed()
        {
            Failed?.Invoke(this, new UnitOfWorkFailedEventArgs(this, _exception, _isRolledback));
        }

        /// <summary>
        /// 释放所有活动事务
        /// </summary>
        /// <remarks>
        /// 遍历并释放所有活动的事务资源
        /// 即使某个事务释放失败，也会继续尝试释放其他事务
        /// </remarks>
        private void DisposeTransactions()
        {
            foreach (var transactionApi in GetAllActiveTransactionApis())
            {
                try
                {
                    transactionApi.Dispose();
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "工作单元 {Id} 释放事务资源时发生异常", Id);
                }
            }
        }

        /// <summary>
        /// 检查工作单元是否已释放
        /// </summary>
        /// <exception cref="ObjectDisposedException">当工作单元已释放时抛出</exception>
        protected void ThrowIfDisposed()
        {
            ObjectDisposedException.ThrowIf(IsDisposed, GetType().Name);
        }

        #endregion IDisposable
    }
}